/*
 *  libKara - Library for basic instant messaging functions
 *  Copyright (C) 2007-2008 Markus Karnik
 *
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 3 of the License, 
 *  or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along 
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
 *  
 */

package de.karnik.libkara;

import java.util.Vector;

import de.karnik.libkara.pluginspecs.BaseProtocol;
import de.karnik.libkara.pluginspecs.ProtocolListener;
import de.karnik.libkara.pluginspecs.ProtocolSettings;
import de.karnik.libkara.proto.ProtocolException;
import de.karnik.libkara.roster.ImContact;
import de.karnik.libkara.roster.ImMessage;
import de.karnik.libkara.roster.ImRoster;

/**
 * The account manager class to manage all im accounts.
 * 
 * @author Markus Karnik - markus.karnik@my-design.org
 * @version 1.0
 * @since 1.0
 */
public class ImAccountManager implements ProtocolListener {

	/**
	 * The account manager listener.
	 */
	private ImAccountManagerListener managerListener = null;
	/**
	 * The account storage.
	 */
	private Vector<ImAccount> accounts = new Vector<ImAccount>( 1, 1 );

	/**
	 * The account manager instance.
	 */
	private static ImAccountManager manager = null;
	
	/**
	 * Returns the account manager instance.
	 * 
	 * @return The account manager instance.
	 */
	public synchronized static ImAccountManager getInstance() {
		
		if( manager == null )
			manager = new ImAccountManager();
		
		return manager;
	}
	
	/**
	 * Standard constructor. Hidden. Caused by the singleton pattern.
	 */
	private ImAccountManager() {}
	
	/**
	 * Adds an account to the manager.
	 * 
	 * @param account The account to add.
	 * @return The position id for the account.
	 */
	public int addAccount( ImAccount account ) {
		
		account.setProtocolListener( this );
		this.accounts.add( account );
		return this.accounts.size() - 1;
	}
	
	/**
	 * Returns the account with the specified id.
	 * 
	 * @param id The id of the wanted account.
	 * @return The wanted account.
	 */
	public ImAccount getAccount( String id ) {
		for( int i = 0; i < this.accounts.size(); i++ )
			if( this.accounts.get( i ).getId().equals( id ) )
				return this.accounts.get( i );
		
		return null;
	}
	
	/**
	 * Returns the all accounts logged in indicator.
	 * 
	 * @return <b>true</b> if all accounts are logged in, <b>false</b> otherwise.
	 */
	public boolean areAllAccountsLoggedIn() {
		boolean allLoggedIn = true;
		
		for( int i = 0; i < this.accounts.size(); i++ )
			if( this.accounts.get( i ).isActive()  
					&& !this.accounts.get( i ).getProtoSettings().getBooleanData( ProtocolSettings.BOOLEAN_VALUE_LOGGED_IN ) ) {
				allLoggedIn = false;
			}
		
		return allLoggedIn;
	}
	
	/**
	 * Returns the all accounts logged off indicator.
	 * 
	 * @return <b>true</b> if all accounts are logged off, <b>false</b> otherwise.
	 */
	public boolean areAllAccountsLoggedOff() {
		boolean allLoggedOff = true;
		
		for( int i = 0; i < this.accounts.size(); i++ )
			if( this.accounts.get( i ).isActive()  
					&& this.accounts.get( i ).getProtoSettings().getBooleanData( ProtocolSettings.BOOLEAN_VALUE_LOGGED_IN ) ) {
				allLoggedOff = false;
			}
		
		return allLoggedOff;
	}
	
	/**
	 * Removes the given account from the manager.
	 * 
	 * @param account The account to remove.
	 */
	public void removeAccount( ImAccount account ) {
		this.accounts.remove( account );
	}

	/**
	 * Removes the account manager listener.
	 */
	public void removeManagerListener() {
		this.managerListener = null;
	}
	
	/**
	 * Changes Status. Logs in or logs off if needed. 
	 * 
	 * @param status The status to set.
	 * @throws ProtocolException
	 */
	public void changeStatus( int status ) throws ProtocolException {
		
		if( status == BaseProtocol.STATUS_OFFLINE ) {
			if( !areAllAccountsLoggedOff() ) {
				logoffAccounts();
			}
		} else {
		
			if( !this.areAllAccountsLoggedIn() ) {
				if( this.managerListener != null )
					this.managerListener.connectionTaskStarted();
				
				for( int i = 0; i < this.accounts.size(); i++ )
					this.accounts.get( i ).getProtocol().proto_setStatus( status );
				
				loginAccounts();
			} else {
				for( int i = 0; i < this.accounts.size(); i++ )
					this.accounts.get( i ).getProtocol().proto_changeStatus( status );
			}
			
		}
		
		if( this.managerListener != null )
			this.managerListener.statusChanged();
	}
	
	/**
	 * Logs in all enabled accounts.
	 * 
	 * @throws ProtocolException
	 */
	public void loginAccounts() throws ProtocolException {
		for( int i = 0; i < this.accounts.size(); i++ ) {
			
			BaseProtocol protocol = this.accounts.get( i ).getProtocol();
			
			if( protocol != null && this.accounts.get( i ).isActive() && !protocol.proto_isConnected() )
				protocol.proto_connect();
				
			if( protocol.proto_isConnected() 
						&& !this.accounts.get( i ).getProtoSettings().getBooleanData( ProtocolSettings.BOOLEAN_VALUE_LOGGED_IN ) )
					protocol.proto_login();
		}
	}
	
	/**
	 * Logs off all enabled accounts.
	 * 
	 * @throws ProtocolException
	 */
	public void logoffAccounts() throws ProtocolException {
		for( int i = 0; i < this.accounts.size(); i++ ) {
			
			BaseProtocol protocol = this.accounts.get( i ).getProtocol();
			
			if( protocol != null && this.accounts.get( i ).isActive() && protocol.proto_isConnected() ) {
				protocol.proto_logoff();
				protocol.proto_disconnect();
			}
		}
		
		ImRoster.getInstance().clearRoster();
		
	}

	/**
	 * Sets the account manager listener.
	 * 
	 * @param managerListener The account manager listener to set.
	 */
	public void setManagerListener( ImAccountManagerListener managerListener ) {
		this.managerListener = managerListener;
	}

	/* (non-Javadoc)
	 * @see de.karnik.libkara.pluginspecs.ProtocolListener#connected(java.lang.String)
	 */
	public void connected( String accountID ) {
		if( areAllAccountsLoggedIn() )
			if( this.managerListener != null )
				this.managerListener.connectionTaskEnded();
		
	}

	/* (non-Javadoc)
	 * @see de.karnik.libkara.pluginspecs.ProtocolListener#disconnected(java.lang.String)
	 */
	public void disconnected( String accountID ) {
		if( this.areAllAccountsLoggedOff() )
			if( this.managerListener != null )
				this.managerListener.connectionDisconnected();
	}

	/* (non-Javadoc)
	 * @see de.karnik.libkara.pluginspecs.ProtocolListener#contactReceived(de.karnik.libkara.roster.ImContact)
	 */
	public void contactReceived( ImContact contact ) {}
	/* (non-Javadoc)
	 * @see de.karnik.libkara.pluginspecs.ProtocolListener#contactStatusChanged(java.lang.String, int, java.lang.String)
	 */
	public void contactStatusChanged( String userID, int status, String statusMessage ) {}
	/* (non-Javadoc)
	 * @see de.karnik.libkara.pluginspecs.ProtocolListener#messageArrived(java.lang.String, de.karnik.libkara.roster.ImMessage)
	 */
	public void messageArrived(String userID, ImMessage message) {}
	/* (non-Javadoc)
	 * @see de.karnik.libkara.pluginspecs.ProtocolListener#rosterReceived()
	 */
	public void rosterReceived() {}
}
